#ifndef _OD_SIP_COMN_H_
#define _OD_SIP_COMN_H_

#ifdef __cplusplus
    extern "C" {
#endif

#include "eXosip2/eXosip2.h" 
#include "pipeline.h"
#include "od_sip_GJB_xml_cmd_define.h"
#include "od_sip_types.h"


#define SIP_NORMAL_ARRAY_SIZE   48


#define ALLOW_STRING                   "REGISTER,INVITE,ACK,CANCEL,OPTIONS,BYE,MESSAGE,UPDATE,INFO"
#define SUPPORT_STRING                 "timer,100rel"
#define SESSION_EXPIRES_STRING         "10"
#define REQUIRE_STRING                 "timer"
#define MIN_SE_STRING                  "90"
#define CONTENT_TYPE_STRING            "application/sdp"
#define Session_Expires                "Session-Expires"
#define SHORT_SE                       "x"      //Session-Expires short define
#define Min_SE                         "Min-SE"
#define Resource_Priority              "Resource-Priority"

typedef struct 
{
    char name[SIP_NORMAL_ARRAY_SIZE];                       /*姓名*/   
    char phone_no[SIP_NORMAL_ARRAY_SIZE];                   /*电话号码*/
    char time[SIP_NORMAL_ARRAY_SIZE];                       /*开始时间 + 通话计时*/
    char time_interval[SIP_NORMAL_ARRAY_SIZE];              /*通话时长*/
    char stat[SIP_NORMAL_ARRAY_SIZE];                       /*状态、结构*/
}sip_call_log_info;


/* Enumeration of refresher */
typedef enum  {
    RFH_UNKNOWN,  //0
    RFH_UAC,      //1
    RFH_UAS,      //2
    RFH_UNSUPPORT //3
}od_sip_timer_refresher;

/**/
typedef enum 
{
    OD_SIP_PROTOCOL_GJB10,
    OD_SIP_PROTOCOL_GB28181_2016,
    OD_SIP_PROTOCOL_NUM
}od_sip_protocol_standard;


/**
 * This structure describes Session Timers settings in an invite session.
 */
typedef struct
{
    /** 
     * Specify minimum session expiration period, in seconds. Must not be
     * lower than 90. Default is 90.
     */
    uint32_t min_se;

    /**
     * Specify session expiration period, in seconds. Must not be lower than
     * #min_se. Default is 1800.
     */
    uint32_t sess_expires;	

    /** session refesher, means who send UPDATE? */
    od_sip_timer_refresher refresher;

    /** last send UPDATE to keep alive timestemp*/
    uint32_t last_refresh_time;

    /** V1.0 std show that, when refresher loss 3-times, end the session!*/
    int refresh_cnt; 

    /** Maximum times of failures,default is 3*/
    int max_fail_times;
    
} od_sip_timer_setting;

typedef struct
{
    od_sip_net_type net_type;        /**< IN_NET type IP4 or IP6*/
    uint64_t audio_codec;            /**< audio codec support a bitmap, see #od_sip_audio_media_type for more infomation*/
    uint64_t video_codec;            /**< video codec support a bitmap, see #od_sip_video_media_type*/
    uint32_t audio_recv_port;        /**< local audio recv port*/
    uint32_t video_recv_port;        /**< local video recv port*/
    uint32_t vid_h264_pt;            /**< video rtp payload type! h264*/
    uint32_t vid_h265_pt;            /**< video rtp payload type! h265*/
    uint32_t vid_javs_pt;            /**< unsupport right now!*/

    /*please see #h264_standard_table, and #h265_standard_table
    * find out which reso you endpoint support! and then write like
    * w=1920, h=1080, fps=30.Don't out of two tables range!
    */
    struct 
    {
        uint32_t w;
        uint32_t h;
        uint32_t fps;
    }reso;

    /** Media transfer should support port reuse, whether enabled
     * should be configurable, and through the mux parameter negotiation, 
     * it is appropriate to turn on by default
    */
    struct 
    {
        pj_bool_t flg;        /**< true:yes, false:no*/
        uint32_t muxid;
    }rtpport_mux;

    uint32_t vid_bitrate;        /**< dec bandwidth*/
    char ipaddr[33];             /**< local ipaddr like 192.168.1.100*/
    od_sip_gb28181_bitrate_type bitrate_type;  /**< video bitrate type,GB28181-2016 CBR or VBR*/
    char ssrc[32];               /**< rtp send ssrc*/
}od_media_config;

typedef enum
{
    PARAM_TYPE_ENCODE,
    PARAM_TYPE_DECODE,
    PARAM_TYPE_NUM
}media_params_type;

typedef struct
{
    media_params_type cfg_type;
    uint32_t sys_bandw;
    char ssrc[32];
    
    struct 
    {
        pj_bool_t flg;
        uint32_t muxid;
    }rtpport_mux;

    struct 
    {
        char aud_ip[33];         /**< audio ip dest*/
        char codec_name[32];     /**< codec type in string, PCMA/PCMU/G729,etc*/
        char dir[32];            /**< transfer direction,sendrecv/sendonly/recvonly/inactive*/
        uint32_t trans_proto;    /**< transaction protocol, RTP/AVP,RTP/SAVP etc*/  
        uint32_t aud_port;       /**< audio media send udp port*/
        uint32_t aud_pt;         /**< audio RTP payload type*/
        uint32_t clock_rate;
        uint32_t bitrate;
    }aud;
    
    struct 
    {
        char vid_ip[33];         /**< video ip dest*/
        char codec_name[32];       /**< codec type in string, H264/H265/JAVC,etc*/
        char dir[32];            /**< transfer direction, sendrecv/sendonly/recvonly/inactive*/
        uint32_t trans_proto;    /**< transaction protocol, RTP/AVP,RTP/SAVP etc*/  
        uint32_t vid_port;       /**< video media send udp port*/
        uint32_t vid_pt;         /**< video RTP payload type*/
        uint32_t bitrate;        /**< represent video bitrate*/
        uint32_t w;              /**< width*/
        uint32_t h;              /**< height*/
        uint32_t fps;            /**< framerate per second*/
        uint32_t clock_rate;
        uint32_t profile_level_id;
    }vid;
}od_media_params;




/**
 * 获取使用此SIP模块的设备或系统的音视频媒体能力，这些能力包含支持哪些种类的音频类型，
 * 比如支持PCMA/PCMU/AAC等，视频支持H264/H265或者JAVS等等，都可以在#capa_cfg这个参数中
 * 体现。这个能力获取函数，获取的是设备支持能力的全集。
 *
 * @param h          user handle.
 * @param capa_cfg   Configuration parameters for device capability, 
 *                   please refer to #demo to find out how to use this parameter .
 *
 * @return              none
 */
typedef void (*on_call_get_capa)(void *h, od_media_config *capa_cfg);


/**
 * 在SIP协商后，SIP模块会得到具体的视音频，网络等参数，通过此回调函数，SIP模块
 * 可以将协商后的参数设置给设备，使用SIP模块者，需要实现此回调函数，否则不能完成
 * 媒体设置。
 *
 * @param h          user handle.
 * @param cfg        media parameters set to decoder or encoder, 
 *                  #media_params_type show is for enc or dec, please check.
 *
 * @return              none
 */
typedef void (*on_call_set_cfg)(void *h, od_media_params *cfg);


/**
 * 这个回调需要在主动发出呼叫即INVITE之前调用，设备需要把本次呼叫的视音频参数
 * 填写到#cfg中去，与#on_call_get_capa区别是，这个函数只获取本次呼叫的你希望建立
 * 起来的参数设置。比如，我希望使用PCMA 和 H265/H264，1080P建立通信，那么在#cfg中，
 * 只填写这相关配置即可。这里的#cfg是能力全集的一小部分
 * 
 *
 * @param h          user handle.
 * @param cfg        media parameters set to decoder or encoder, 
 *                  #media_params_type show is for enc or dec, please check.
 *
 * @return              none
 */
typedef void (*on_call_get_cfg)(void *h, od_media_config *cfg);


/**
* 完成通话记录回写的功能。 
*
* @param h          user handle.
* @param cfg        日志信息，用户可以把这些信息提取走，写你自己定义的日志
*
* @return              none
*/
typedef void (*on_call_call_log)(void *h, sip_call_log_info *call_log);


/**
* 复位编解码器，一般在一个会话结束后调用，调用此回调时，希望完成的功能是关闭
* 编码，解码，关闭发送，此时编解码器处于静默复位状态。与编解码相关的开关应关闭。
*
* @param h          user handle.
*
* @return              none
*/
typedef void (*on_call_reset_codec)(void *h);


/**
* 更新I帧的回调函数。调用此函数后，编码器应该立刻发出一个IDR 关键参考帧。
*
* @param h          user handle.
*
* @return              none
*/
typedef void (*on_call_keyframe)(void *h);


/**
* SIP模块收到MESSAGE请求后，会调用此回调函数来给出云台控制指令，
* 回调函数收到#cmd指令后，完成云台相关一系列控制响应。
*
* @param h          user handle.
* @param cmd        云台控制参数
* 
* @return              none
*/
typedef void (*on_call_ptzctl)(void *h, ptzctrl *cmd);


/**
* 设置I帧间隔
*
* @param h          user handle.
* @param cmd        I帧间隔参数
* 
* @return              none
*/
typedef void (*on_call_set_gop)(void *h, msg_iframeconfreq *cmd);


/**
* SIP模块收到MESSAGE请求后，会调用此回调函数来给云台设置或者删除一个预制位。
*
* @param h          user handle.
* @param cmd        云台预置位参数
* 
* @return              none
*/
typedef void (*on_call_ptzposition)(void *h, ptzposition *cmd);


/**
* 设置一条OSD显示指令的回调函数
* 
* @param h          user handle.
* @param cmd        OSD具体参数，包含字体，颜色，内容等等
* 
* @return              none
*/
typedef void (*on_call_set_osd)(void *h, msg_osd *cmd);      


/**
* 设置多行OSD显示指令的回调函数。最大支持8条
* 
* @param h          user handle.
* @param cmd        OSD具体参数，包含字体，颜色，内容等等
* 
* @return              none
*/
typedef void (*on_call_set_osds)(void *h, msg_osd_multi *cmd); /*set multi lines OSD*/


/**
* 设置自定义字幕的回调函数。可以使用此回调做一些协议外的扩展
* 
* @param h          user handle.
* @param cmd        OSD具体参数，包含字体，颜色，内容等等
* 
* @return              none
*/
typedef void (*on_call_set_customosd)(void *h, msg_osd_customosd *cmd); 


typedef struct 
{
    int transport;              /**< transport protocol, IPPROTO_UDP or IPPROTO_TCP*/
    int local_sip_port;         /**< local sip port default is 5060*/
    int family;                 /**< address family: AF_INET6 or AF_INET*/

    /**< Signaling timeout timer */
    struct 
    {
        int T1;
        int T2;
        int T4;
    }timer;

    int trace_log_lvl;         /**< trace log level for debug*/
    od_sip_protocol_standard protocol_standard;
}od_sip_engine_cfg;


/**
* register node number
*/
typedef enum{
    REG_NODE_1,
    REG_NODE_2,
    REG_NODE_NUM
}reg_node_no;

typedef struct
{
    /*register write, other thread read!!*/
    struct
    {
        char *username;
        char *proxy_addr;
        char *passwd;
        int *proxy_port;
        int *reg_status;         
        int init_done;
        /*FIXME:need a lock?*/
    }acc_info[REG_NODE_NUM]; //account infomation

    od_sip_timer_setting se_timer;  //session timer.用于会话保链
    
}od_sip_share;


/**
* @brief od sip module call-back function ptr.
*/
typedef struct
{
    struct 
    {
        on_call_get_capa get_capa;
        on_call_set_cfg set_cfg;
        on_call_get_cfg get_cfg;
        void *h_callback;
    }cfg_cb;

    struct
    {
        on_call_reset_codec reset_codec;
        void *h_codec;
    }codec_cb;

    struct
    {   
        on_call_keyframe keyframe;
        void *h_keyframe;
    }keyframe_cb;

    struct
    {
        on_call_ptzctl ptzctl;
        void *h_ptzctl;
        on_call_ptzposition ptzpos;
        void *h_ptzpos;
    }ptzctl_cb;

    struct
    {
        on_call_set_gop gop_set;
        void *h_gop;
    }gop_set_cb;

    struct
    {
        on_call_set_osd osd;
        void *h_osd;
        on_call_set_osds osds;
        void *h_osds;
        on_call_set_customosd customosd;
        void *h_customosd;
    }osd_cb;

    struct
    {
        on_call_call_log call_log;
        void *h_call_log;
    }log_cb;

}od_sip_callback;


/**
 * @brief od sip 上下文结构体，很关键的共享数据结构，
 * 保存了ex_ctx, pj_pool,还有回调函数指针等关键
 * 数据。
 */	
typedef struct 
{
    eXosip_t *ex_ctx;
    pj_pool_t *pool;
    od_sip_share *share;
    od_sip_callback cb;
    od_sip_engine_cfg engine_cfg;
}od_sip_ctx;


#ifndef release
    #define OD_SIP_ASSERT_RETURN(expr,retval)    \
    	    do { \
    		if (!(expr)) { assert(expr); return retval; } \
    	    } while (0)

    #define OD_SIP_ASSERT(expr)    \
    	    do { \
    		if (!(expr)) { assert(expr);} \
    	    } while (0)    
#else
    #define OD_SIP_ASSERT_RETURN(expr,retval)    \
    	    do { \
    		if (!(expr)) { return retval; } \
    	    } while (0)

    #define OD_SIP_ASSERT(expr)    \
	        do { \
		    if (!(expr)) { return;} \
	        } while (0)
#endif

/**
 * 初始化osip/eXosip库，和pjsip库，并且注入一些用户自定义的回调函数，
 * 这些回调函数将用于协商后的参数设置，日志记录，参数获取，云台控制等
 * 功能。
 *
 * @param cfg        主要配置传输协议和本地sip端口，可以写NULL使用默认参数
 * @param cb         回调函数注入.
 *
 * @return           成功返回OD_OK,否则返回OD_ERROR
 */	    
int od_sip_comn_init(od_sip_engine_cfg *cfg, od_sip_callback *cb);


/**
 * 释放osip/eXosip库，和pjsip库
 */	
void od_sip_comn_deinit();


/**
 * 每个任务都要拿这个返回的上下文
 */	
od_sip_ctx *od_sip_lib_start();


/**
 * 任务退出要释放归还资源
 */	
void od_sip_lib_stop();

/**
 * 获取当前系统时间
 */	
uint32_t od_sip_getCurTimeInMsec();

/**
 * 清空fifo中留存的event事件
 */	
void od_sip_clear_event_fifo(pipeline_handle fifo);


/**
 * 获取osip/eXosip返回的事件，SIP模块是按照事件驱动架构的。
 *
 * @param pipe       从哪个管道获取事件
 * @param event      获取到的事件指针
 *
 * @return           成功返回OD_OK,否则返回OD_ERROR
 */	
int od_sip_get_event(pipeline_handle pipe, eXosip_event_t **event);


/**
 * 返回ACK，这个是在会话中的ACK
 *
 * @param exctx      eXosip 上下文
 * @param event      获取到的事件指针
 *
 * @return           none
 */
void od_sip_send_ack(eXosip_t *exctx, eXosip_event_t *event);


/**
 * 在会话中返回一个带状态码status的响应
 *
 * @param exctx      eXosip 上下文
 * @param event      获取到的事件指针
 * @param status     状态码
 *
 * @return           none
 */
void od_sip_call_send_answer_with_code(eXosip_t *exctx, eXosip_event_t *event, int status);


/**
 * 在会话外返回一个带状态码status的响应
 *
 * @param exctx      eXosip 上下文
 * @param event      获取到的事件指针
 * @param status     状态码
 *
 * @return           none
 */
void od_sip_send_answer_with_code(eXosip_t *exctx, eXosip_event_t *event, int status);


#ifdef __cplusplus
    }
#endif

#endif

/*_*/

